#include <athapascan-1.h>
#include <online-certifier/user-functions/userIncludes.hh>
#include <online-certifier/util/generatePrime.inl>
#include <online-certifier/util/typeDefines.hh>
#include <vector>
#include <fstream> 
#include <sstream>
#include <exception>


using namespace std;



struct do_ParallelModComputation{

    void operator()(string outputDir, size_t first, size_t last, size_t grain,  string functionName, string args){
        try{


            if((last - first) <= grain){ // perform sequential calls if remained tasks are less than the user grain
    
                //tmp solution (kaapi 2.4  doesnt support stl vector as it claims)
                stringstream tmp;
                tmp << args;
                string line;
                vector<string> v;
                do{
                    getline(tmp,line);
                    if(!line.empty())
                    v.push_back(line);
                }while(!tmp.eof());
                // finish of the tmp solution

                userModularFunctionBase* f = userModularFunctionBase::initializeByName(functionName, v);
                stringstream  fileName;
                fileName << outputDir << '/' << first << '-' << last;

                vector<fixedBitSizeInteger> primeTab;
                generate21bitsprimes(&primeTab, first, last);
                { 
                    int i ; 
                    int diff = last-first;
                    for (i = 0; i<diff ; ++i)
                        f->userModularFunction(primeTab.at(i));
                    
                }
          
                //writing the output to a file
            
                ofstream file;
                file.open(fileName.str().c_str());
                
                if(file.is_open()){
                    file <<'$';
                    file << *f;
                    file << flush;
    
                    file.seekp(0,ios::beg);
                    file.put('\n');
                    file.close();
                }
                else
                    cerr << "[grid] failed to open " << fileName.str() <<endl;
               
                delete f;
                
            }else if (first < last){
        
                size_t m = (last + first )/2  ;

                a1::Fork<do_ParallelModComputation>()(outputDir, first, m, grain,  functionName, args);
                a1::Fork<do_ParallelModComputation>()(outputDir,m, last, grain, functionName, args);
            }

        }catch(const char* err){
            cerr << "[Grid] user function error: " <<endl;
            cerr << err << endl;

        }catch(exception& ex){

            cerr << "[Grid] error:" <<endl;
            cout << ex.what() <<endl;
        }
       // }catch(...){
          // cerr << "[Grid] unexpected grid exception!" <<endl;
        //}
        //
    }


};

struct do_main{

    void operator()(int argc, char** argv){
        if(argc < 7){
            cerr << "[Grid] wrong Arguments!" <<endl;
            cerr << "Usage: kaapiTask output_dir first last grain_size functionName args" <<endl;
            return;
        }
        
        string output_dir = argv[1];
       
        
        size_t first = atol(argv[2]);
        size_t last = atol(argv[3]);
        
        size_t grain = atol(argv[4]);
        string functionName =  argv[5];
                
        stringstream args;
        for (int i=6; i < argc;i++)
            args << argv[i] << '\n';
   
        a1::Fork<do_ParallelModComputation>()(output_dir, first, last, grain, functionName, args.str());
    }

};

int main(int argc, char** argv){


    a1::Community com = a1::System::join_community(argc,argv); // init library
    a1::ForkMain<do_main>()(argc,argv); // main call

    com.leave(); // ensure no more task left
    
    a1::System::terminate(); // clean up

    return 0;


}
